/*
 * SSH Assistant - Helper for SSH key, agent, and tunnel management.
 *
 * Copyright (c) 2005-2006 Justin Mecham <justin@aspect.net>
 * Copyright (c) 2003 Michael K Link <mlink@synthemesc.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#import "PassphraseUI.h"

#include <unistd.h>
#include <Foundation/Foundation.h>

extern NSString * serviceString;

@implementation PassphraseUI

- (IBAction) cancelBtnAct:(id)sender
{
	[[NSApplication sharedApplication] terminate:self];
}

- (IBAction)okBtnAct:(id)sender
{
	const char * serviceName  = [serviceString cString];
	const char * accountName  = "sshAssistant";
	
	OSStatus secErr;
	UInt32 passwordLength     = [[passphrase stringValue] length];
	const char * passwordData = [[passphrase stringValue] cString];
	
	secErr = SecKeychainAddGenericPassword(NULL, strlen(serviceName), serviceName, strlen(accountName), accountName, 
				                           passwordLength, passwordData, NULL);
	
	// ignore duplicate errors
	if (secErr && (secErr != errSecDuplicateItem))
	{
		NSRunAlertPanel(@"Security Error", [self errorString:secErr], nil, nil, nil);
	}
	
	// Print password out to ssh agent
	printf("%s\n", passwordData);

	// Shutdown agent
	[[NSApplication sharedApplication] terminate:self];
}

- (void) go
{
    /* This is REALLY ugly... */
	NSString * argument = [[[NSProcessInfo processInfo] arguments] objectAtIndex:1];
	NSRange startOfPath = [argument rangeOfString:@"/"];
	NSRange endOfPath = [argument rangeOfString:@":"];
	NSString * keyPath = [argument substringWithRange:NSMakeRange(startOfPath.location, endOfPath.location - startOfPath.location)];
	NSString * prompt = [[NSString alloc] initWithFormat:@"Please provide your passphrase for the SSH private key '%@':", keyPath];
	[displayTxt setStringValue:prompt];
    
	const char * serviceName = [serviceString cString];
	const char * accountName = "sshAssistant";

	SecKeychainSetUserInteractionAllowed(TRUE);

	OSStatus secErr;
	UInt32 passwordLength = 512;
	char * passwordData = (char*)malloc(passwordLength);
	
	if ([[[[NSProcessInfo processInfo] arguments] objectAtIndex:1] hasPrefix:@"Bad "])
	{		
		printf("%s\n", passwordData);		
		free(passwordData);
		goto go_end;
	}
		
  go_again:
	secErr = SecKeychainFindGenericPassword(NULL, strlen(serviceName), serviceName, strlen(accountName), accountName, &passwordLength, (void**) &passwordData, NULL);
	
	if (!secErr) 
	{ 
		// No error, we got a good password
		passwordData[passwordLength] = '\0';
		printf("%s\n", passwordData);
		free(passwordData);
		[[NSApplication sharedApplication] terminate:self];
	}
	else if (secErr == errSecBufferTooSmall) 
	{
		// Password buffer was too smal
		passwordLength *= 2;		
		if (passwordData = (char*) realloc(passwordData, passwordLength)) {
			goto go_again;
		}
	}		
	else 
	{
		// Something else went wrong
		NSRunAlertPanel(@"Security Error", [self errorString:secErr], nil, nil, nil);
	}

  go_end:	
	[theWindow center];
	[theWindow makeKeyAndOrderFront:self];
}

- (NSString *) errorString:(OSStatus)secErr
{
	switch (secErr)
    {
		case errSecNotAvailable:			return @"No trust results are available.";
		case errSecReadOnly:				return @"Read only error.";
		case errSecAuthFailed:				return @"Authorization/Authentication failed.";
		case errSecNoSuchKeychain:			return @"The keychain does not exist.";
		case errSecInvalidKeychain:			return @"The keychain is not valid.";
		case errSecDuplicateKeychain:		return @"A keychain with the same name already exists.";
		case errSecDuplicateCallback:		return @"More than one callback of the same name exists.";
		case errSecInvalidCallback:			return @"The callback is not valid.";
		case errSecDuplicateItem:			return @"The item already exists.";
		case errSecItemNotFound:			return @"The item cannot be found.";
		case errSecBufferTooSmall:			return @"The buffer is too small.";
		case errSecDataTooLarge:			return @"The data is too large.";
		case errSecNoSuchAttr:				return @"The attribute does not exist.";
		case errSecInvalidItemRef:			return @"The item reference is invalid.";
		case errSecInvalidSearchRef:		return @"The search reference is invalid.";
		case errSecNoSuchClass:				return @"The keychain item class does not exist.";
		case errSecNoDefaultKeychain:		return @"A default keychain does not exist.";
		case errSecInteractionNotAllowed:	return @"Interaction is not allowed with the Security Server.";
		case errSecReadOnlyAttr:			return @"The attribute is read only.";
		case errSecWrongSecVersion:			return @"The version is incorrect.";
		case errSecKeySizeNotAllowed:		return @"The key size is not allowed.";
		case errSecNoStorageModule:			return @"There is no storage module available.";
		case errSecNoCertificateModule:		return @"There is no certificate module available.";
		case errSecNoPolicyModule:			return @"There is no policy module available.";
		case errSecInteractionRequired:		return @"User interaction is required.";
		case errSecDataNotAvailable:		return @"The data is not available.";
		case errSecDataNotModifiable:		return @"The data is not modifiable.";
		case errSecCreateChainFailed:		return @"The attempt to create a certificate chain failed.";
		case errSecACLNotSimple:			return @"The access control list is not in standard simple form.";
		case errSecPolicyNotFound:			return @"The policy specified cannot be found.";
		case errSecInvalidTrustSetting:		return @"The trust setting is invalid.";
		case errSecNoAccessForItem:			return @"The specified item has no access control.";
		default:							return [NSString stringWithFormat:@"Unknown error type %d", secErr];
	}
}

- (void) sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
	[[NSApplication sharedApplication] terminate:self];
}

@end